package template;

import java.util.ArrayList;

import logist.simulation.Vehicle;
import logist.task.Task;
import logist.topology.Topology.City;

public class VehicleTasks {

	private Vehicle vehicle;
	private ArrayList<TaskAction> taskActionList;
	private double totalCost;


	public VehicleTasks(Vehicle vehicle) {
		this.vehicle = vehicle;
		this.taskActionList = new ArrayList<TaskAction>();
		totalCost = 0;
	}

	public VehicleTasks(Vehicle vehicle, ArrayList<TaskAction> taskActionList) {
		this.vehicle = vehicle;
		this.taskActionList = new ArrayList<TaskAction>(taskActionList);
		totalCost = this.calcTotalCost();
	}

	public VehicleTasks(VehicleTasks oldVehicleTasks) {
		this.vehicle = oldVehicleTasks.vehicle;
		this.taskActionList = new ArrayList<TaskAction>(oldVehicleTasks.taskActionList);
		this.totalCost = oldVehicleTasks.totalCost;
	}

	public void updateTotalCost() {
		this.totalCost = this.calcTotalCost();
	}

	public double getTotalCost() {
		return totalCost;
	}

	public ArrayList<TaskAction> getTaskActionList() {
		return this.taskActionList;
	}

	public void addTaskActionPair(Task task){
		int i = (int)(Math.random() * (taskActionList.size()+1));
		int j = i + 1 + (int)(Math.random() * (taskActionList.size()+1-i));
		this.taskActionList.add(i, new TaskAction(task, task.pickupCity, true));
		this.taskActionList.add(j, new TaskAction(task, task.deliveryCity, false));
		this.updateTotalCost();
	}

	public void addTaskAction(TaskAction taskAction ){
		this.taskActionList.add(taskAction);
		this.updateTotalCost();
	}

	public void addTaskAction(int index, TaskAction taskAction ){
		this.taskActionList.add(index, taskAction);
		this.updateTotalCost();
	}

	public double calcTotalCost() {
		double cost = 0;

		City currentCity = vehicle.getCurrentCity();
		for (int i = 0; i < taskActionList.size(); i++) {
			TaskAction taskAction= taskActionList.get(i);
			cost += currentCity.distanceTo(taskAction.getLocation()) * vehicle.costPerKm() ;
			currentCity = taskAction.getLocation();
		}

		return cost;
	}


	public TaskAction getTaskAction(Task task, boolean isPickup) {
		for (int i = 0; i < taskActionList.size(); i++) {
			TaskAction taskAction= taskActionList.get(i);
			if (taskAction.getTaskId() == task.id && taskAction.isPickUp() == isPickup) 
				return taskAction;
		}

		return null;
	}


	public boolean isValid(){
		int carried = 0;
		for (int i = 0; i < taskActionList.size(); i++) {
			TaskAction taskAction= taskActionList.get(i);

			if (taskAction.isPickUp()) {
				carried += taskAction.getTask().weight;
				if (carried > vehicle.capacity()){
					return false;
				}
			} else {
				carried -= taskAction.getTask().weight;
			}
		}
		return true;
	}

	int findPositionInTaskActionList(ArrayList<TaskAction> list, int taskId, boolean isPickUp){
		for (int i = 0; i < list.size(); i++) {
			TaskAction taskAction = list.get(i);
			if (taskAction.getTaskId() == taskId && taskAction.isPickUp() == isPickUp) {
				return i;
			}
		}		
		return -1;
	}


	public ArrayList<VehicleTasks> getNeighboursChangeOrder(){
		ArrayList<VehicleTasks> list = new ArrayList<VehicleTasks>();

		for (int i = 0; i < taskActionList.size()-1; i++) {
			for (int j = i+1; j < taskActionList.size(); j++) {
				ArrayList<TaskAction> newTaskActionList = new ArrayList<TaskAction>(taskActionList);
				if (newTaskActionList.get(i).isPickUp()){
					// find delivery i position k
					// if k <= j
					// error
					int k = findPositionInTaskActionList(newTaskActionList, newTaskActionList.get(i).getTaskId(), false);
					if (k <= j) 
						continue;
				}

				TaskAction temp = newTaskActionList.get(i);
				newTaskActionList.set(i, newTaskActionList.get(j));
				newTaskActionList.set(j, temp);

				ArrayList<VehicleTasks> listTemp = new ArrayList<VehicleTasks>();
				if (newTaskActionList.get(i).isPickUp()) {
					int k = findPositionInTaskActionList(newTaskActionList, newTaskActionList.get(i).getTaskId(), false);
					TaskAction ta = newTaskActionList.get(k);
					newTaskActionList.remove(k);
					for (int l = i+1; l <= newTaskActionList.size(); l++) {
						VehicleTasks vt = new VehicleTasks(vehicle, newTaskActionList);
						vt.addTaskAction(l, ta);
						if (vt.isValid()) 
							listTemp.add(vt);
					}
				}
				else {
					int k = findPositionInTaskActionList(newTaskActionList, newTaskActionList.get(i).getTaskId(), true);
					TaskAction ta = newTaskActionList.get(k);
					newTaskActionList.remove(k);
					for (int l = 0; l < i; l++) {
						VehicleTasks vt = new VehicleTasks(vehicle, newTaskActionList);
						vt.addTaskAction(l, ta);
						if (vt.isValid()) 
							listTemp.add(vt);
					}					
				}
				if (listTemp.size() == 0)
					continue;

				VehicleTasks minCostVehicleTasks = listTemp.get(0);
				double minCost = minCostVehicleTasks.getTotalCost();

				for (int l = 1; l < listTemp.size(); l++) {
					double currentCost = listTemp.get(l).getTotalCost();
					if ( currentCost < minCost ){
						minCost = currentCost;
						minCostVehicleTasks = listTemp.get(l);
					}
				}
				list.add(minCostVehicleTasks);				
			}
		}


		return list;
	}


	public ArrayList<VehicleTasks> moveFirstTaskTo(VehicleTasks toVehicleTasks){

		ArrayList<VehicleTasks> list = new ArrayList<VehicleTasks>();
		VehicleTasks tempFromVehicleTasks = new VehicleTasks(this);

		TaskAction pickUpAction = tempFromVehicleTasks.getTaskActionList().get(0);
		tempFromVehicleTasks.taskActionList.remove(0);
		int taskId = pickUpAction.getTaskId();
		ArrayList<TaskAction> tempList = tempFromVehicleTasks.taskActionList;
		TaskAction deliveryAction = null;
		int deliveyTaskPosition = -1;
		for (int i = 0; i < tempList.size(); i++) {
			if (tempList.get(i).getTaskId() == taskId && !tempList.get(i).isPickUp() ){
				deliveryAction = tempList.get(i);
				deliveyTaskPosition = i;
			}
		}
		tempFromVehicleTasks.taskActionList.remove(deliveyTaskPosition);
		ArrayList<VehicleTasks> listTemp = new ArrayList<VehicleTasks>();

		if (tempFromVehicleTasks.taskActionList.size() > 0) {
			TaskAction newPickUpAction = tempFromVehicleTasks.getTaskActionList().get(0);
			int newTaskId = newPickUpAction.getTaskId();
			tempList = tempFromVehicleTasks.taskActionList;
			TaskAction newDeliveryAction = null;
			int newDeliveryTaskPosition = -1;
			for (int i = 1; i < tempList.size(); i++) {
				if (tempList.get(i).getTaskId() == newTaskId && !tempList.get(i).isPickUp() ){
					newDeliveryAction = tempList.get(i);
					newDeliveryTaskPosition = i;
				}
			}
			tempFromVehicleTasks.taskActionList.remove(newDeliveryTaskPosition);

			for (int i = 1; i <= tempFromVehicleTasks.taskActionList.size(); i++) {
				VehicleTasks newFromVehicleTasks = new VehicleTasks(tempFromVehicleTasks);
				newFromVehicleTasks.addTaskAction(i, newDeliveryAction);
				if (newFromVehicleTasks.isValid()) 
					listTemp.add(newFromVehicleTasks);
			}

			VehicleTasks minFromVehicleTasks = listTemp.get(0);
			double min = minFromVehicleTasks.getTotalCost();

			for (int i = 1; i < listTemp.size(); i++) {
				double currentCost = listTemp.get(i).getTotalCost();
				if ( currentCost < min ){
					min = currentCost;
					minFromVehicleTasks = listTemp.get(i);
				}
			}		
			list.add(minFromVehicleTasks);
		}
		else
			list.add(tempFromVehicleTasks);

		if (toVehicleTasks.taskActionList.size() == 0) {
			VehicleTasks newToVehicleTasks = new VehicleTasks(toVehicleTasks);
			newToVehicleTasks.addTaskAction(pickUpAction);
			newToVehicleTasks.addTaskAction(deliveryAction);
			if (newToVehicleTasks.isValid()) {
				list.add(newToVehicleTasks);
				return list;
			} else {
				return new ArrayList<VehicleTasks>();
			}
		} else {
			listTemp = new ArrayList<VehicleTasks>();
			for (int i = 1; i <= toVehicleTasks.taskActionList.size()+1; i++) {
				VehicleTasks newToVehicleTasks = new VehicleTasks(toVehicleTasks);
				newToVehicleTasks.addTaskAction(0, pickUpAction);
				newToVehicleTasks.addTaskAction(i, deliveryAction);
				if (newToVehicleTasks.isValid()) 
					listTemp.add(newToVehicleTasks);
			}
		}

		if (listTemp.size() == 0) {
			return new ArrayList<VehicleTasks>();
		}

		VehicleTasks minCostVehicleTasks = listTemp.get(0);
		double minCost = minCostVehicleTasks.getTotalCost();

		for (int i = 1; i < listTemp.size(); i++) {
			double currentCost = listTemp.get(i).getTotalCost();
			if ( currentCost < minCost ){
				minCost = currentCost;
				minCostVehicleTasks = listTemp.get(i);
			}
		}

		list.add(minCostVehicleTasks);

		return list;
	}


}
